#include <stdio.h>
#include <wchar.h>
#include <math.h>
#include <string>
#include <ft2build.h>
#include <iostream>
#include <functional>
#include <locale>
#include <codecvt>
#include FT_FREETYPE_H

using namespace std;

class CsTTF{
public:
	CsTTF(string path,int sz=32){ initial(path,sz); };
	~CsTTF(){ free(); };
private:
	string       m_filepath = "";
	FT_Library   m_lib;
	FT_Face      m_fac;
	FT_Vector    m_pen;
	FT_GlyphSlot m_slt;
	FT_Matrix    m_mat = {0x10000L,0,0,0x10000L};
	int			 m_err = 0;
	int			 m_nsz = 32;
	bool		 m_INI = false;

	struct tagCharImg{
		unsigned char *p = nullptr;
		int			   x = 0;
		int			   y = 0;
		int			   w = 0;
		int			   h = 0;
	};
private:
	void initial(string path,int sz=32){
		if( m_INI ){ return; } 
		if( m_err = FT_Init_FreeType(&m_lib) ){ return; }
		if( m_err = FT_New_Face(m_lib,path.c_str(),0,&m_fac) ){
		}else{
			m_nsz = sz>0?sz:32;
			FT_Set_Pixel_Sizes(m_fac,m_nsz,0);
			m_slt   = m_fac->glyph;
			m_pen.x = m_pen.y = 0;
			m_INI   = true;
		}
	}

	void free(){	
		if( !m_INI ){ return; }
		FT_Done_Face(m_fac);
		FT_Done_FreeType(m_lib);
	}
public:
	int CharImage(wchar_t c, std::function<void(unsigned char*,int,int,int,int)>cb = nullptr ){
	    if( !m_INI ){ return -1; }
		FT_Set_Transform(m_fac,&m_mat, &m_pen);
	    m_err = FT_Load_Char(m_fac,c,FT_LOAD_RENDER);
		if(cb){ cb(m_slt->bitmap.buffer,m_nsz-m_slt->bitmap.width+m_slt->bitmap_left,m_nsz - m_slt->bitmap_top,m_slt->bitmap.width,m_slt->bitmap.rows); }
		return m_err;	
	}

	int WStrImage_H(const wchar_t *s,std::function<void(unsigned char*,int,int)>cb = nullptr,int Hgap = 3 ){
		if( !m_INI ){ return -1; }
		int nLen =  wcslen(s);
		if( nLen>0 ){
			int		   rw   = -1;
			int		   rh   = -1;
			int        miny = m_nsz;
			tagCharImg * pGroup = new tagCharImg[nLen];
			
			for( int i=0; i<nLen; i++ ){
				CharImage( s[i],[&pGroup,i,Hgap](unsigned char* p,int x,int y,int w, int h){

					//cout << x << " " << y << " " << w << " " << h << endl;
					int nSz = w*h;
					pGroup[i].w = w;
					pGroup[i].h = h;
					pGroup[i].x = Hgap;
					pGroup[i].y = y;
					pGroup[i].p = new unsigned char[nSz];
					memcpy(pGroup[i].p,p,nSz);
				});
				if( m_err){break;}
				else{
					if( miny>pGroup[i].y ){ miny = pGroup[i].y; }
					if( i==0 ){ pGroup[i].x =0; rw = pGroup[i].w; rh = pGroup[i].y+pGroup[i].h; }
					else{ 
						pGroup[i].x += pGroup[i-1].x + pGroup[i-1].w; 
						if(rh<pGroup[i].y+pGroup[i].h){ rh = pGroup[i].y+pGroup[i].h; }
					}
					rw = pGroup[nLen-1].w + pGroup[nLen-1].x;
				}
			}

			rh -= miny;
			if( rw>0 && rh>0 ){
				auto * img = new unsigned char[rw*rh];
				memset(img,0,rw*rh);
				for( int i=0; i<nLen; i++ ){
					auto *p = pGroup[i].p;
					int   x = pGroup[i].x;
					int   y = pGroup[i].y - miny;
					int	  w = pGroup[i].w;
					int   h = pGroup[i].h;
					//cout << x << " " << y << " " <<  w  << " " <<  h << endl;
					for( int sy = 0; sy<h; sy++){
						auto * src = p+sy*w;
						auto * dst = img+(y+sy)*rw+x;
						for( int sx = 0; sx<w; sx++){
							(*dst) |= (*src);
							dst++;
							src++;
						}
					}
					delete []p;
				}
				if(cb){ cb(img,rw,rh); }
				delete[]img;
			}
			delete []pGroup;
		}
		
		return m_err;
	}

};

std::wstring string2wString(const std::string& s)
{
    std::string strLocale = setlocale(LC_ALL, "");
    const char* chSrc = s.c_str();
    size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1;
    wchar_t* wchDest = new wchar_t[nDestSize];
    wmemset(wchDest, 0, nDestSize);
    mbstowcs(wchDest,chSrc,nDestSize);
    std::wstring wstrResult = wchDest;
    delete []wchDest;
    setlocale(LC_ALL, strLocale.c_str());
    return wstrResult;
}


/*usage example
./charFont Phaser小站 >  charImg.h
./charFont Phaser小站 >> charImg.h
*/
int main(int argc, char **argv)
{
	string  ss      = "";
	wstring new_str = L"";
	string  fntfile = "STHUPO.TTF";
	int 	sz      = 48;
	
	std::wstring_convert<std::codecvt_utf8<wchar_t>> w_converter;
	if( argc<2 ){ cout << "command format: charFont string [font size=48] [font file path=STHUPO.TTF] [ > out file name]" ;return 0;}
	if( argc>1 ){ ss = argv[1]; new_str = string2wString(ss);}
	if( argc>2 ){ string temp = argv[2]; sz = atoi(temp.c_str()); if(sz<1){sz=1;} }
	if( argc>3 ){ fntfile     = argv[3]; }

	CsTTF ttf(fntfile,sz);
	ttf.WStrImage_H(new_str.c_str(),[&new_str](unsigned char *img,int rw,int rh){
		std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
		cout << endl << "//" << myconv.to_bytes(new_str) << endl;
		cout << "unsigned char img_" << hash<wstring>()(new_str) << "[" << rh << "]" << "[" << rw << "] = {" << endl;
		for( int j=0; j< rh; j++ ){
			cout << "{";
			for( int i=0; i<rw; i++ ){
				if( img[j*rw+i]>0 ){ cout << 1; }
				else{ cout << 0; }
				if(i<rw-1){ cout << ","; }
			}
			cout << "}"; if(j<rh-1){ cout << ","; } cout << endl;
		}
		cout << "};" << endl;
	});
	return 0;
}